package cn.i7baozh.cloud.gateway.predicate;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.AfterRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

/**
 * 仿照AfterRoutePredicateFactory自定义一个断言器： 要求请求接口中必须有userLevel参数，且值必须为在gateway中配置的
 * 例如： 配置了 - MyOwn=abc
 * 则只能通过：localhost:9527/pay/custom/test?userLevel=abc 访问，其他userLevel不等于abc的时候都不让访问
 * 类名必须为 xxxRoutePredicateFactory
 *
 * @author zhangbaoqi532
 * @date 2024/12/27
 * @see AfterRoutePredicateFactory
 */
@Component // 必须要注册到spring里去
public class MyOwnRoutePredicateFactory extends AbstractRoutePredicateFactory<MyOwnRoutePredicateFactory.Config> {

    /**
     * 必须提供一个无参的构造方法
     */
    public MyOwnRoutePredicateFactory() {
        super(MyOwnRoutePredicateFactory.Config.class);
    }

    /**
     * 返回true允许通过
     * false不允许
     *
     * @param config
     * @return
     */
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        //打印配置
        System.out.println("--------------config:" + config);
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                String userLevel = serverWebExchange.getRequest().getQueryParams().getFirst("userLevel");
                if (userLevel == null || userLevel.trim().isEmpty()) {
                    return false;
                }
                if (config.getAllowUserLevel().equals(userLevel)) {
                    return true;
                }
                return false;
            }
        };
    }

    /**
     * 这个方法的意思是告诉程序怎么解析你的配置并将他们赋值到你的config里去
     * 例如你的config需要2个字段飞别为allowUserLevel和status
     * 那么你就需要再配置中配置2个值，两个值就要用英文逗号分隔
     * - MyOwn=abc,123
     * 此时这个方法你就要返回Lists.newArrayList("allowUserLevel","status")，这样你在apply方法中获取到的config就会有这2个值
     * 如果你返回的是Lists.newArrayList("allowUserLevel22","status"),字段名跟config里定义的对不上，那你只能获取到config里的status值，allowUserLevel无法获取到
     *
     * @return
     */
    @Override
    public List<String> shortcutFieldOrder() {
        List<String> list = new ArrayList<>();
        list.add("allowUserLevel");
        list.add("status");
        return list;
        //config里只有一个参数
//        return Collections.singletonList("allowUserLevel");
    }


    public static class Config {

        private String allowUserLevel;

        private Integer status;

        public String getAllowUserLevel() {
            return allowUserLevel;
        }

        public void setAllowUserLevel(String allowUserLevel) {
            this.allowUserLevel = allowUserLevel;
        }

        public Integer getStatus() {
            return status;
        }

        public void setStatus(Integer status) {
            this.status = status;
        }

        @Override
        public String toString() {
            return "Config{" +
                    "allowUserLevel='" + allowUserLevel + '\'' +
                    ", status=" + status +
                    '}';
        }
    }
}
